import history from 'history/browser';
import { ILocalStore } from 'hooks/useLocalStore';
import { action, computed, IReactionDisposer, makeObservable, observable, reaction, runInAction } from 'mobx';
import { normalizeRepoSearchResult, RepoSearchResultApi, RepoSearchResultModel } from 'store/models';
import RootStore from 'store/RootStore';
import ApiStore, { FetchMeta } from 'store/RootStore/ApiStore';
import SearchPageStore from 'store/SearchPageStore';

import { searchCreate } from 'utils';

type PrivateFields = '_fetchRequestsMeta' | '_searchOrgReposMeta' | '_searchOrgRepoResult' | '_hashElement';

type OrganizationReposRequest = {
    organization: string;
    type?: string;
    perPage?: number;
    currentPage?: number;
};

export default class OrgReposContainerStore implements ILocalStore {
    private readonly _ApiStore = new ApiStore();
    private _searchOrgReposMeta: FetchMeta = FetchMeta.INITIAL;
    private _searchOrgRepoResult: RepoSearchResultModel[] = [];
    private _hashElement: HTMLDivElement | null = null;
    private readonly _fetchMetaReaction: IReactionDisposer;
    private readonly _newPageReaction: IReactionDisposer;
    private readonly _setHashRefReaction: IReactionDisposer;
    private readonly _searchPageStore: SearchPageStore;

    constructor(searchPageStore: SearchPageStore) {
        makeObservable<OrgReposContainerStore, PrivateFields>(this, {
            _searchOrgReposMeta: observable,
            _searchOrgRepoResult: observable.ref,
            _hashElement: observable,
            _fetchRequestsMeta: computed,
            orgRepos: computed,
            setHashElement: action.bound,
            fetchOrganizationRepos: action.bound,
            onClickSearchOwnerHandle: action.bound,
        });

        this._searchPageStore = searchPageStore;
        const searchParams = new URLSearchParams(window.location.search);
        const inputInit = RootStore.locationStore.getSearchParams('search') ?? searchParams.get('search');
        const pageInit = RootStore.locationStore.getSearchParams('page') ?? searchParams.get('page');
        const typeInit = RootStore.locationStore.getSearchParams('types') ?? searchParams.get('types');

        if (inputInit?.length) {
            history.replace({
                pathname: '/',
                search: searchCreate({
                    search: inputInit,
                    types: typeInit,
                    page: pageInit,
                }),
            });
            this.fetchOrganizationRepos({
                organization: inputInit,
                currentPage: pageInit ? Number(pageInit) : 1,
                type: typeInit ?? '',
            });
        }

        this._newPageReaction = reaction(
            () => this._searchOrgRepoResult,
            () => this._searchPageStore.resultsElement?.scrollIntoView({ behavior: 'smooth', block: 'end' }),
        );

        this._fetchMetaReaction = reaction(
            () => this._fetchRequestsMeta,
            (meta) => {
                RootStore.pageLoader.getFetchRequestsMeta(meta);
            },
        );

        this._setHashRefReaction = reaction(
            () => this._hashElement,
            (hashElement) => {
                if (hashElement) {
                    hashElement.scrollIntoView({ behavior: 'instant', block: 'center' });
                }
            },
        );
    }

    private get _fetchRequestsMeta(): FetchMeta[] {
        return [this._searchOrgReposMeta];
    }
    get orgRepos(): RepoSearchResultModel[] {
        return this._searchOrgRepoResult;
    }
    setHashElement = (ref: HTMLDivElement | null): void => {
        this._hashElement = ref;
    };

    onClickSearchOwnerHandle = (): void => {
        this._searchPageStore.setCurrentPage(1);
        this.fetchOrganizationRepos({
            organization: RootStore.locationStore.getSearchParams('search') ?? '',
            currentPage: 1,
            type: RootStore.locationStore.getSearchParams('types'),
        });
    };

    fetchOrganizationRepos = async (data: OrganizationReposRequest): Promise<void> => {
        this._searchOrgReposMeta = FetchMeta.FETCHING;
        const { organization, currentPage, perPage = 9, type } = data;

        try {
            const result = await this._ApiStore.getApiRequest<RepoSearchResultApi[]>(`orgs/${organization}/repos`, {
                params: {
                    type: type,
                    page: currentPage,
                    per_page: perPage,
                    direction: 'desc',
                },
            });
            runInAction(() => {
                this._searchOrgRepoResult = normalizeRepoSearchResult(result.data);
                let page = result.headers['link'] as string | undefined;
                page = page?.match(/(?:rel="next")(.+)(?=rel="last")/)?.[1];
                page = page?.match(/(?:&page=)(\d+)/)?.[1];
                if (!page) {
                    page = String(currentPage);
                }
                this._searchPageStore.setPageCount(page ? Number(page) : undefined);
                this._searchOrgReposMeta = FetchMeta.FETCHED;
            });
        } catch {
            runInAction(() => {
                this._searchOrgReposMeta = FetchMeta.ERROR;
                this._searchOrgRepoResult = [];
                this._searchPageStore.setPageCount(undefined);
            });
        }
    };

    destroy(): void {
        this._fetchMetaReaction();
        this._setHashRefReaction();
        this._newPageReaction();
    }
}
